

-- Namespace
	local _,mod = ...


-- Did it fail?
	if not mod.newbutton then
		return
	end


-- Object cache
	local newbutton = mod.newbutton
	local pulse = mod.pulse
	local blacklist = mod.blacklist


-- Common
	local active = {}
	local order = {}


-- Infrastructure
	local recycle = {}
	
	local function new(spell,exp,duration,icon,type,link)
		local btn = recycle[#recycle]
		if btn then
			recycle[#recycle] = nil
			btn:Show()
		else
			btn = newbutton()
		end
		
		btn.__spell = spell
		btn.__icon = icon
		btn.__exp = exp
		btn.__duration = durationn
		btn.__type = type
		btn.__link = link
		
		btn:Update('cooldown',spell,icon,type,link)
		btn:Update('time',exp,duration)
		
		local id = #order+1
		btn.__id = id
		btn:Update('order',id)
		
		active[spell] = id
		order[id] = btn
	end
	
	local function update(spell,exp,duration)
		local btn = order[active[spell]]
		btn.__exp = exp
		btn.__duration = duration
		btn:Update('time',exp,duration)
	end
	
	local function done(spell)
		local id = active[spell]
		local btn = order[id]
		
		tremove(order,id)
		active[spell] = nil
		
		if pulse then
			pulse:Update('pulse',btn.__spell,btn.__icon,btn.__type,btn.__link)
		end
		
		btn.__spell = nil
		btn.__icon = nil
		btn.__exp = nil
		btn.__duration = nil
		btn.__type = nil
		btn.__link = nil
		
		btn:Update('cooldown',nil)
		
		recycle[#recycle+1] = btn
		btn:Hide()
		
		local b
		for i = id,#order do
			b = order[i]
			b.__id = i
			active[b.__spell] = i
		end
	end
	
	local function reorder()
		local b
		for i = 1,#order do
			b = order[i]
			b:Update('order',i)
		end
	end


-- Internal timer
	local function pendulum()
		local changed = false
		
		local now = GetTime()
		
		local btn,exp,duration
		
		local i = 1
		repeat
			btn = order[i]
			exp = btn.__exp-now
			if exp <= 0 then
				done(btn.__spell)
				changed = true
			else
				i = i + 1
			end
		until i > #order
		
		if changed then
			if next(active) then
				reorder()
			else
				mod:timer_unregister(pendulum)
			end
		end
	end


-- Cooldowns
	local care_spells = {}
	local care_petspells = {}
	local care_items = {}
	local care_inventory = {}
	
	local function SPELL_UPDATE_COOLDOWN()
		local spell,start,duration,icon,_
		
		local changed = false
		
		for i = 1,#care_spells do
			spell = care_spells[i]
			start,duration = GetSpellCooldown(spell)
			if start > 0 and duration > 1 then
				if active[spell] then
					update(spell,start+duration,duration)
				elseif duration > 2 then
					_,_,icon = GetSpellInfo(spell)
					new(spell,start+duration,duration,icon,'spell',GetSpellLink(spell))
					changed = true
				end
			elseif active[spell] then
				done(spell)
				changed = true
			end
		end
		
		if changed then
			if next(active) then
				reorder()
				mod:timer_register(.1,true,pendulum)()
			else
				mod:timer_unregister(pendulum)
			end
		end
	end
	
	mod:event_register('SPELL_UPDATE_COOLDOWN',SPELL_UPDATE_COOLDOWN)
	
	
	local function PET_BAR_UPDATE_COOLDOWN()
		local spell,start,duration,icon,_
		
		local changed = false
		
		for i = 1,#care_petspells do
			spell = care_petspells[i]
			start,duration = GetSpellCooldown(spell)
			if start > 0 and duration > 1 then
				if active[spell] then
					update(spell,start+duration,duration)
				elseif duration > 2 then
					_,_,icon = GetSpellInfo(spell)
					new(spell,start+duration,duration,icon,'pet',GetSpellLink(spell))
					changed = true
				end
			elseif active[spell] then
				done(spell)
				changed = true
			end
		end
		
		if changed then
			if next(active) then
				reorder()
				mod:timer_register(.1,true,pendulum)()
			else
				mod:timer_unregister(pendulum)
			end
		end
	end
	
	mod:event_register('PET_BAR_UPDATE_COOLDOWN',PET_BAR_UPDATE_COOLDOWN)
	
	
	local function BAG_UPDATE_COOLDOWN()
		local item,bag,slot,start,duration,icon
		
		local changed = false
		
		for i = 1,#care_items do
			item = care_items[i]
			bag,slot,item = item[1],item[2],item[3]
			start,duration = GetContainerItemCooldown(bag,slot)
			if start > 0 and duration > 1 then
				if active[item] then
					update(item,start+duration,duration)
				elseif duration > 2 then
					icon = GetContainerItemInfo(bag,slot)
					new(item,start+duration,duration,icon,'item',select(2,GetItemInfo(item)))
					changed = true
				end
			elseif active[item] then
				done(item)
				changed = true
			end
		end
		
		if changed then
			if next(active) then
				reorder()
				mod:timer_register(.1,true,pendulum)()
			else
				mod:timer_unregister(pendulum)
			end
		end
	end
	
	mod:event_register('BAG_UPDATE_COOLDOWN',BAG_UPDATE_COOLDOWN)
	
	
	local function INVENTORY_UPDATE_COOLDOWN()
		local item,inv,start,duration,icon
		
		local changed = false
		
		for i = 1,#care_inventory do
			inv = care_inventory[i]
			item = tonumber(GetInventoryItemLink('player',inv):match('|Hitem:(%d+)'))
			start,duration = GetInventoryItemCooldown('player',inv)
			if start > 0 and duration > 1 then
				if active[item] then
					update(item,start+duration,duration)
				elseif duration > 2 then
					icon = GetInventoryItemTexture('player',inv)
					new(item,start+duration,duration,icon,'inventory',select(2,GetItemInfo(item)))
					changed = true
				end
			elseif active[item] then
				done(item)
				changed = true
			end
		end
		
		if changed then
			if next(active) then
				reorder()
				mod:timer_register(.1,true,pendulum)()
			else
				mod:timer_unregister(pendulum)
			end
		end
	end
	
	mod:event_register('BAG_UPDATE_COOLDOWN',INVENTORY_UPDATE_COOLDOWN)


-- Spell tracking events
	local function SPELLS_CHANGED()
		table.wipe(care_spells)
		table.wipe(care_petspells)
		
		local spell,sptype
		
		-- General tab
		local _,_,offset,num = GetSpellTabInfo(1)
		for i = offset+1,offset+num do
			sptype,spell = GetSpellBookItemInfo(i,BOOKTYPE_SPELL)
			if spell and sptype == 'SPELL' then
				spell = GetSpellInfo(spell)
				if spell and not blacklist[spell] then
					care_spells[#care_spells+1] = spell
				end
			end
		end
		
		-- Spec tab
		-- GetSpellTabInfo(2) always means the active spec tab right now. May change later.
		local _,_,offset,num = GetSpellTabInfo(2)
		for i = offset+1,offset+num do
			sptype,spell = GetSpellBookItemInfo(i,BOOKTYPE_SPELL)
			if spell and sptype == 'SPELL' then
				spell = GetSpellInfo(spell)
				if spell and not blacklist[spell] then
					care_spells[#care_spells+1] = spell
				end
			end
		end
		
		-- Pet spells
		if PetHasSpellbook() then
			for i = 1,(HasPetSpells() or 0) do
				sptype,spell = GetSpellBookItemInfo(i,BOOKTYPE_PET)
				if spell and sptype == 'SPELL' then
					spell = GetSpellInfo(spell)
					if spell and not blacklist[spell] then
						care_petspells[#care_petspells+1] = spell
					end
				end
			end
		end
		
		SPELL_UPDATE_COOLDOWN()
		PET_BAR_UPDATE_COOLDOWN()
	end
	
	mod:event_register('SPELLS_CHANGED',function()
		mod:timer_register(1,false,SPELLS_CHANGED)  -- Very spammy, so throttle it.
	end)
	mod:event_register('LEARNED_SPELL_IN_TAB',SPELLS_CHANGED)
	mod:event_register('PLAYER_SPECIALIZATION_CHANGED',SPELLS_CHANGED)
	mod:event_register('PLAYER_ENTERING_WORLD',SPELLS_CHANGED)
	
	
	local function ITEMS_CHANGED()
		table.wipe(care_items)
		
		local item
		
		for bag = 0,4 do
			for slot = 1,GetContainerNumSlots(bag) do
				item = GetContainerItemLink(bag,slot)
				if item then
					item = tonumber(item:match('|Hitem:(%d+)'))
					if item and not blacklist[item] then
						care_items[#care_items+1] = {bag,slot,item}
					end
				end
			end
		end
		
		BAG_UPDATE_COOLDOWN()
	end
	
	mod:event_register('BAG_UPDATE',function()
		mod:timer_register(1,false,ITEMS_CHANGED)  -- Very spammy, so throttle it.
	end)
	mod:event_register('PLAYER_ENTERING_WORLD',ITEMS_CHANGED)
	
	
	local function INVENTORY_CHANGED()
		table.wipe(care_inventory)
		
		local inv
		
		for i = 1,19 do
			inv = GetInventoryItemLink('player',i)
			if inv then
				inv = tonumber(inv:match('|Hitem:(%d+)'))
				if inv and not blacklist[inv] then
					care_inventory[#care_inventory+1] = i
				end
			end
		end
		
		INVENTORY_UPDATE_COOLDOWN()
	end
	
	mod:event_register('UNIT_INVENTORY_CHANGED',function()
		mod:timer_register(1,false,INVENTORY_CHANGED)  -- Very spammy, so throttle it.
	end)
	mod:event_register('PLAYER_ENTERING_WORLD',INVENTORY_CHANGED)
